//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
//==================================================================

#include "stdafx.h"
#include "gxy_base.h"
#include "gxy_sprite.h"

//==================================================================
using namespace PUtils;

//==================================================================
static void imagebaseFitsquare( PUtils::ImageBase *bp, int x1, int y1, int w, int h, GXY::Rect *out_rectp );

//==================================================================
namespace GXY {

//==================================================================
///
//==================================================================

//==================================================================
///
//==================================================================
SpriteManager::SpriteManager() :
	_texturep(NULL),
	_own_texture(false),
	_blendtype(PSYSGFX::BASICBLEND_TRANSPARENT),
	_texflags(SPRITE::TEXFLG_BILINEAR),
	_n_sprites(0),
	_max_w(0),
	_max_h(0)
{
}

//==================================================================
SpriteManager::SpriteManager( ImageBase *imgbasep ) :
	_texturep(NULL),
	_own_texture(false),
	_blendtype(PSYSGFX::BASICBLEND_TRANSPARENT),
	_texflags(SPRITE::TEXFLG_BILINEAR),
	_n_sprites(0),
	_max_w(0),
	_max_h(0)
{
	_texturep = new TextureBase( imgbasep, GetRenderDevice() );
}

//==================================================================
SpriteManager::SpriteManager( SpriteManager *fromp ) :
	_texturep(NULL),
	_own_texture(false),
	_blendtype(PSYSGFX::BASICBLEND_TRANSPARENT),
	_texflags(SPRITE::TEXFLG_BILINEAR),
	_n_sprites(0),
	_max_w(0),
	_max_h(0)
{
	SpriteManager( fromp->_texturep->GetImageBase() );
}

//==================================================================
SpriteManager::SpriteManager( const TCHAR *fnamep ) :
	_texturep(NULL),
	_own_texture(false),
	_blendtype(PSYSGFX::BASICBLEND_TRANSPARENT),
	_texflags(SPRITE::TEXFLG_BILINEAR),
	_n_sprites(0),
	_max_w(0),
	_max_h(0)
{
	_texturep = new TextureBase( fnamep, NULL, GetRenderDevice() );
}

//==================================================================
SpriteManager::~SpriteManager()
{
	for (int i=0; i < _sprite_pools.len(); ++i)
	{
		delete [] _sprite_pools[ i ];
	}

	if ( _own_texture )
		SAFE_DELETE( _texturep );
}

//==================================================================
Sprite	*SpriteManager::AddSprite( int x, int y, int w, int h, int odx, int ody ) throw(...)
{
	int idx_in_last_pool = _n_sprites % SPRITES_PER_POOL;

	Sprite	*spritep = NULL;

	if ( idx_in_last_pool == 0 )
	{
		Sprite	*poolp = new Sprite[SPRITES_PER_POOL];
		_sprite_pools.push_back( poolp );
		++_n_sprites;
		spritep = &poolp[0];
	}
	else
	{
		Sprite	*poolp = _sprite_pools[ _sprite_pools.len()-1 ];
		++_n_sprites;
		spritep = &poolp[ idx_in_last_pool ];
	}

	spritep->_originp = this;
	spritep->_u = x;
	spritep->_v = y;
	spritep->_w = w;
	spritep->_h = h;
	spritep->_odx = odx;
	spritep->_ody = ody;

	float scoe = _texturep->GetSTexRatio();
	float tcoe = _texturep->GetTTexRatio();
	spritep->_s1 = (x) * scoe;
	spritep->_t1 = (y) * tcoe;
	spritep->_s2 = (x + w) * scoe;
	spritep->_t2 = (y + h) * tcoe;

	return spritep;
}

//==================================================================
Sprite *SpriteManager::GetSprite( u_int idx )
{
	PASSERT( idx < _n_sprites );
	int	pool_idx = idx / SPRITES_PER_POOL;
	int	sprt_idx = idx & (SPRITES_PER_POOL-1);

	return _sprite_pools[ pool_idx ] + sprt_idx;
}

//==================================================================
void SpriteManager::BuildOneSprite( int odx, int ody )
{
	ImageBase	*bp = _texturep->GetImageBase();

	AddSprite( 0, 0, bp->GetWidth(), bp->GetHeight(), odx, ody );
}

//==================================================================
void SpriteManager::BuildOneSpriteAlign( int align_x, int align_y )
{
	ImageBase	*bp = _texturep->GetImageBase();

	int	w = bp->GetWidth();
	int	h = bp->GetHeight();

	AddSprite(	0, 0, w, h,
				-w*0.5f * (align_x + 1),
				-h*0.5f * (align_y + 1) );
}

//==================================================================
u_int SpriteManager::BuildSpritesFromSize( int itm_w, int itm_h,
										  int offx, int offy,
										  int odx, int ody,
								          int max_ch_x, int max_ch_y,
										  int max_tot, bool do_fit )
{
int				nx, ny, ny_run;
int				ix, iy;
int				ch_cnt, max_tot_cnt;
Rect			fit_rect;
int				min_y, max_y;
ImageBase		*bp;

	bp = _texturep->GetImageBase();

	nx = bp->GetWidth() / itm_w;
	ny = bp->GetHeight() / itm_h;

	if ( max_tot <= 0 )
		max_tot = 99999999;

	if ( max_ch_x <= 0 )
		max_ch_x = 99999999;

	if ( max_ch_y <= 0 )
		max_ch_y = 99999999;

	if ( max_ch_x < nx )
		nx = max_ch_x;

	if ( max_ch_y < ny )
		ny = max_ch_y;

	// used only for fitting
	min_y = bp->GetHeight();
	max_y = 0;

	//----------------------------------------------------------------
	if ( do_fit )
	{
		max_tot_cnt = max_tot;
		ny_run = ny;

		for (iy=0; iy < ny_run; ++iy)
		{
			int base_y = offy + iy * itm_h;
			if ( base_y + itm_h > bp->GetHeight() )
				break;

			ch_cnt = max_ch_x;
			for (ix=0; ix < nx; ++ix)
			{
				int	relative_y1, relative_y2;

				if ( offx + ix * itm_w + itm_w > bp->GetWidth() )
				{
					++ny_run;
					break;
				}

				fit_rect._x = offx + ix * itm_w;
				fit_rect._y = base_y;
				fit_rect._w = itm_w;
				fit_rect._h = itm_h;

				imagebaseFitsquare( bp, fit_rect._x, fit_rect._y, fit_rect._w, fit_rect._h, &fit_rect );

				relative_y1 = fit_rect._y - base_y;
				relative_y2 = fit_rect._y + fit_rect._h-1 - base_y;

				if ( relative_y1 < min_y )	min_y = relative_y1;
				if ( relative_y2 > max_y )	max_y = relative_y2;

				if ( --ch_cnt <= 0 )
					break;

				if ( --max_tot_cnt <= 0 )
				{
					if ( max_y - min_y + 1 > _max_h )
						_max_h = max_y - min_y + 1;
				
					goto out;
				}
			}
		}
		
		if ( max_y - min_y + 1 > _max_h )
			_max_h = max_y - min_y + 1;
	}
	else
	{
		if ( itm_h > _max_h )
			_max_h = itm_h;

		if ( itm_w > _max_w )
			_max_w = itm_w;
	}

out:



	//----------------------------------------------------------------
	max_tot_cnt = max_tot;
	ny_run = ny;

	int first_item = -1;
	for (iy=0; iy < ny_run; ++iy)
	{
		int base_y = offy + iy * itm_h;

		if ( base_y + itm_h > bp->GetHeight() )
			break;

		ch_cnt = max_ch_x;
		for (ix=0; ix < nx; ++ix)
		{
			if ( offx + ix * itm_w + itm_w > bp->GetWidth() )
			{
				++ny_run;
				break;
			}

			fit_rect._x = offx + ix * itm_w;
			fit_rect._y = base_y;
			fit_rect._w = itm_w;
			fit_rect._h = itm_h;

			if ( do_fit )
			{
				imagebaseFitsquare( bp, fit_rect._x, fit_rect._y, fit_rect._w, fit_rect._h, &fit_rect );

				if ( fit_rect._w > _max_w )
					_max_w = fit_rect._w;

				fit_rect._y = base_y + min_y;
				fit_rect._h = base_y + max_y - fit_rect._y + 1;
			}

			if ( first_item == -1 )
				first_item = _n_sprites;

			AddSprite( fit_rect._x, fit_rect._y, fit_rect._w, fit_rect._h, odx, ody );

			if ( --ch_cnt <= 0 )
				break;

			if ( --max_tot_cnt <= 0 )
				return first_item;
		}
	}

	return first_item;
}

//==================================================================
};


//==================================================================
static void imagebaseFitsquare( ImageBase *bp, int x1, int y1, int w, int h, GXY::Rect *out_rectp )
{
	int		x, y;
	int		x2, y2;
	int		min_x, min_y, max_x, max_y;

	y2 = y1 + h;
	x2 = x1 + w;
	
	min_x = x2-1;
	min_y = y2-1;
	max_x = x1;
	max_y = y1;

	int alpha_chidx = bp->FindChannelByType( ImageBase::Channel::TYPE_ALPHA );

	for (y=y1; y < y2; ++y)
	{
		for (x=x1; x < x2; ++x)
		{
			if ( bp->GetChanU8( alpha_chidx, x, y ) )
			{
				if ( x < min_x )	min_x = x;
				if ( y < min_y )	min_y = y;
				if ( x > max_x )	max_x = x;
				if ( y > max_y )	max_y = y;
			}
		}
	}

	// special case.. no pixels found
	if ( (max_x - min_x) <= 0 || (max_y - min_y) <= 0 )
	{
		out_rectp->_x = x1;
		out_rectp->_y = y1;
		out_rectp->_w = 1;
		out_rectp->_h = 1;
	}
	else
	{
		out_rectp->_x = min_x;
		out_rectp->_y = min_y;
		out_rectp->_w = max_x - min_x + 1;
		out_rectp->_h = max_y - min_y + 1;
	}
}
